package com.example.datajpa.specification;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.Field;
import java.util.ArrayList;

import static com.google.common.collect.Iterables.toArray;

/**
 * 自定义Specification
 * @author ljw
 * @description
 * @date 2022/6/6 7:34
 */
public class CustomerSpecs {

    public static <T> Specification<T> byAuto(final EntityManager entityManager, final T example) {
        //获取当前实体类对象类的类型
        final Class<T> type = (Class<T>) example.getClass();
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //新建Predicate 列表存储构造的查询条件
                ArrayList<Predicate> predicates = new ArrayList<>();
                //获得实体类的EntityType,我们可以从 EntityType 获得实体类的属性
                EntityType<T> entityType = entityManager.getMetamodel().entity(type);
                //对实体类的所有属性做循环
                for (Attribute<T, ?> attr : entityType.getDeclaredAttributes()) {
                    //获得实体类对象某一个属性的值
                    Object attrValue = getValue(example, attr);
                    if (attrValue != null) {
                        //当前属性值为字符类型
                        if (attr.getJavaType() == String.class) {
                            //属性字符串不为空
                            if (!StringUtils.isEmpty(attrValue)) {
                                //构造当前属性 like（前后%）属性值查询条件，并添加到条件列表中
                                predicates.add(criteriaBuilder.like(root.get(attribute(entityType, attr.getName(), String.class)), pattern((String) attrValue)));
                            }
                        } else {
                            //其余情况下，构造属性和属性值 equal 查询条件，并添加到条件列表中
                            predicates.add(criteriaBuilder.equal(root.get(attribute(entityType, attr.getName(), attrValue.getClass())), attrValue));
                        }
                    }
                }
                return predicates.isEmpty() ? criteriaBuilder.conjunction() : criteriaBuilder.and(toArray(predicates, Predicate.class));
            }

            /**
             * 通过反射获得实体类对象对应属性的属性值
             * @param example 实体类对象
             * @param attr 属性
             * @author ljw
             * @date 2022/6/6 7:59
             */
            private <T> Object getValue(T example, Attribute<T, ?> attr) {
                return ReflectionUtils.getField((Field) attr.getJavaMember(), example);
            }

            /**
             * 获得实体类的当前属性的 SingularAttribute， SingularAttribute包含的是实体类的某个单独属性
             * @param entityType 实体类的EntityType
             * @param fieldName 字段名
             * @param fieldClass 字段类型
             * @author ljw
             * @date 2022/6/8 7:49
             */
            private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entityType, String fieldName, Class<E> fieldClass) {
                return entityType.getDeclaredSingularAttribute(fieldName, fieldClass);
            }

        };
    }

    /**
     * 构造 like 的查询模式，即前后加%
     * @param str 值
     * @author ljw
     * @date 2022/6/8 7:56
     */
    private static String pattern(String str) {
        return "%" + str + "%";
    }


}
